#include <iostream>
#include <cstdio>
#include <event2/buffer.h>
#include <event2/keyvalq_struct.h>
#include "default_handle_http_request.h"
#include "http_request.h"
#include "http_request_dispatcher.h"
#include "constants.h"

static const std::string RESPONSE_FORMAT = "{\"code\":%d, \"reason\":\"%s\", \"content\":\"%s\"}";

static HttpRequest& adapt_http_request(const evhttp_request* ev_req, HttpRequest& http_req);
static void send_http_response(const evhttp_request* ev_req, const HttpResponse& http_resp);

void default_handle_http_request(evhttp_request* ev_req, void* args)
{
    using namespace std;
    cout << "=====================================default_handle_http_request begin....." << endl;

    //convert evhttp_request to HttpRequest
    HttpRequest http_req;
    http_req = adapt_http_request(ev_req, http_req);

    //optional filter:mime/decode/encode 
    
    //dispatch controller and get the response
    HttpController& controller = HttpRequestDispatcher::GetInstace().HandleRequestMapping(http_req.pathInfo);
    
    HttpResponse http_resp;
    
    //assert controller not null
    if (static_cast<HttpController*>(&controller))
    {
        http_resp = controller.DoHttpPost(http_req);
    } 
    else
    {
        http_resp.code = HTTP_NOTFOUND;
        http_resp.reason = "the url handler not found";
        http_resp.content = "";
    }
    
    //reply to the client
    send_http_response(ev_req, http_resp);
    cout << "=====================================default_handle_http_request end....." << endl;
}

static void send_http_response(const evhttp_request* ev_req, const HttpResponse& http_resp)
{
    //response header
    evkeyvalq* headers = evhttp_request_get_output_headers(const_cast<evhttp_request*>(ev_req));
    evhttp_add_header(headers, "Content-Type", "application/json;charset=UTF-8");
    evbuffer* outbuf = evhttp_request_get_output_buffer(const_cast<evhttp_request*>(ev_req));
    //format the output
    evbuffer_add_printf(outbuf, RESPONSE_FORMAT.c_str(), http_resp.code, http_resp.reason.c_str(), http_resp.content.c_str());
    evhttp_send_reply(const_cast<evhttp_request*>(ev_req), http_resp.code, http_resp.reason.c_str(), outbuf);
}

static HttpRequest& adapt_http_request(const evhttp_request* ev_req, HttpRequest& http_req)
{
    using namespace std;
    //convert request method
    switch (evhttp_request_get_command(ev_req))
    {
    case EVHTTP_REQ_GET:
        http_req.method = "GET";
        break;
    case EVHTTP_REQ_POST:
        http_req.method = "POST";
        break;
    default:
        break;
    }
    cout << "http_req.method : " << http_req.method << endl;

    //convert url
    const char* uri = evhttp_request_get_uri(ev_req);
    http_req.requestUrl = string(const_cast<char*>(uri));
    cout << "http_req.requestUrl:" << http_req.requestUrl << endl;


    evhttp_uri* decoded = evhttp_uri_parse(uri);
    if (!decoded)
    {
        evhttp_send_error(const_cast<evhttp_request*>(ev_req), HTTP_BADREQUEST, 0);
    }

    const char* path = evhttp_uri_get_path(decoded);
    if (path)
    {
        http_req.pathInfo = path;
    }
    cout << "req path info: " << http_req.pathInfo << endl;

    //query_str 
    const char* query_str = evhttp_uri_get_query(decoded);
    if (query_str)
    {
        http_req.queryStr = query_str;
        evkeyvalq key_val;
        evhttp_parse_query_str(query_str, &key_val);

        for (evkeyval* p = key_val.tqh_first; p != nullptr; p = p->next.tqe_next)
        {
            http_req.requestParams.insert(pair<string, string>(p->key, p->value));
        }
    }
    evhttp_uri_free(decoded);
    cout << "req queryStr info: " << http_req.queryStr << endl;

    //headers
    evkeyvalq* headers = evhttp_request_get_input_headers(const_cast<evhttp_request*>(ev_req));
    for (evkeyval* p = headers->tqh_first; p != nullptr; p = p->next.tqe_next)
    {
        http_req.headers.insert(pair<string, string>(p->key, p->value));
    }
    
    //body
    evbuffer* inbuf = evhttp_request_get_input_buffer(const_cast<evhttp_request*>(ev_req));
    char buf[MAX_BODY_SIZE] = { 0 };
    while (evbuffer_get_length(inbuf) > 0)
    {
        int n = evbuffer_remove(inbuf, buf, sizeof(buf) - 1);
        if (n > 0)
        {
            buf[n] = '\0';
            http_req.body = buf;
        }
    }
    cout << "req body: " << http_req.body << endl;
    return http_req;
}